#include <sstream>

#include <profo/Protein.h>
#include <profo/unittest.h>
#include <profo/util.h>

using namespace profo;

/**
 * Moves an amino acid with ID = argId in direction specified in argMove.
 *
 * WARNING: Use this method carefuly, and check validity of the Protein after
 * you call it.
 *
 * Here is a description of moves
 * @param arg An input value describing which move to do.
 * 0 - move east          4 - move west
 * 1 - move north-east    5 - move south-west
 * 2 - move north         6 - move south
 * 3 - move north-west    7 - move south-east
 */
void Protein::move(unsigned int argId, unsigned char argMove) {
  AminoAcid& aa = _chain.at(argId);
  switch (argMove) {
    case 0: // east
      aa.x = aa.x + 1;
      break;
    case 1: // north east
      aa.x = aa.x + 1;
      aa.y = aa.y + 1;
      break;
    case 2: // north
      aa.y = aa.y +1;
      break;
    case 3: // north-west
      aa.x = aa.x - 1;
      aa.y = aa.y + 1;
      break;
    case 4: // west
      aa.x = aa.x - 1;
      break;
    case 5: // south west
      aa.x = aa.x - 1;
      aa.y = aa.y -1;
      break;
    case 6: // south
      aa.y = aa.y -1;
      break;
    case 7: // south east
      aa.x = aa.x + 1;
      aa.y = aa.y -1;
      break;
  } // switch
} // move() method

/**
 * This class holds all necessary information about protein, and provides
 * useful methods to alter underlying protein structure, and get vital information
 * needed by VIEW and CONTROL objects.
 *
 * @author Dejan Lekic <dejan.lekic@kcl.ac.uk>
 */
std::string Protein::energyString() const {
  std::ostringstream o;
  o << "Energy: " << std::setprecision(5) << _energy;
  return o.str();
} // energyString() method

/**
 * Typical method for converting the object to
 * a string.
 * Complexity: Linear, O(n), where n denotes a number of AminoAcids in the Protein
 * object.
 */
std::string Protein::toString() const {
  std::string ret;
  if (size() == 0) {
    return ret;
  } else {
    AminoAcid o;
    for (int i=0; i<size();i++) {
      o = _chain[i];
      ret = ret + o.toString() + ";";
    } // for
    return ret;
  } // else
} // toString() method

/**
 * Appends given amino acid ti the protein chain.
 */
void Protein::append(const AminoAcid& arg) {
  _chain.push_back(arg);
} // append() method

/**
 * A method to parse give input string and generate a protein chain.
 *
 * TODO: Add check for exception, becuase it is possible that we run out of
 * memory.
 *
 * Complexity: O(n) where n is equal to arg.length() (size of the input string).
 */
void Protein::parseInput(const std::string& arg) {
  _chain.clear();
  // Every amino acid we are going to insert into the chain is going to have
  // disabled left(west) and right(east) moves. Later on we will fix the first
  // and last amino acid. :)
  unsigned char freeMoves = 255;
  bitOff(0, freeMoves);
  bitOff(4, freeMoves);

  // find how many acids do we actually have
  int count = 0;
  for (int i = 0; i<arg.length(); i++) {
    if ((arg[i] == '1')
      || (arg[i] == 'H')
      || (arg[i] == 'h')
      || (arg[i] == '0')
      || (arg[i] == 'P')
      || (arg[i] == 'p'))
    {
      count++;
    } // if
  } // for
  _chain.resize(count);
  if (arg.empty()) return;
  AminoAcid tmp;
  tmp.free = freeMoves;
  int idx = 0;
  for (int i = 0; i<arg.length(); i++) {
    if ((arg[i] == '1') || (arg[i] == 'H') || (arg[i] == 'h')) {
      tmp.h = true;
      tmp.x = idx+1; // initial X coordinate is 1
      tmp.y = 1;     // initial Y coordinate is 1
      tmp.id = idx;
      _chain[idx] = tmp;
      idx++;
    }
    if ((arg[i] == '0') || (arg[i] == 'P') || (arg[i] == 'p')) {
      tmp.h = false;
      tmp.x = idx+1; // same here, we start from 1
      tmp.y = 1;
      tmp.id = idx;
      _chain[idx] = tmp;
      idx++;
    } // if
  } // for
  if (count > 1) { // do we have 2 or more amino acids in the protein?
     AminoAcid& first = _chain[0];
     unsigned char tmpFree = first.free;
     bitOn(4, tmpFree); // set the left move as a free one for the first amino acid
     first.free = tmpFree;
     AminoAcid& last = _chain[count-1];
     tmpFree = last.free;
     bitOn(0, tmpFree);
     last.free = tmpFree;
  } // if
  if (count == 1) { // do we have only one amino acid?
     AminoAcid& first = _chain[0];
     unsigned char tmpFree = first.free;
     bitOn(4, tmpFree); // set the left move as a free one for the first amino acid
     bitOn(0, tmpFree);
     first.free = tmpFree;
  } // if
} // parseInput() method

/**
 * Tests if given locateion (argX, argY) is adjacent to the given amino acid with
 * ID = argAcid.
 *
 * Following formula relates distance D between two points (x1,y1) and (x2,y2):
 *  2          2          2
 * D  = (x2-x1)  + (y2-y1)
 * In rectangular grid D between two adjacent locations is always 1.
 * @param argX X-coordinate of the test location.
 * @param argY Y-coordinate of the test location.
 * @param argAcid An ID of the test amino acid.
 * @returns true if adjacent, false if not.
 */
bool Protein::adjacentTo(int argX, int argY, int argId) {
  if (_chain.size() < 2) // if protein is empty or has 1 amino acid
    return false;
  bool ret = false;
  AminoAcid& aa = _chain.at(argId);
  int d2 = (aa.x-argX)*(aa.x-argX) + (aa.y-argY)*(aa.y-argY);
  return (d2 == 1);
} // adjacentTo() method

/**
 * A version of parseInput() which takes a C-string as an argument.
 */
void Protein::parseInput(const char* arg) {
  std::string str(arg);
  parseInput(str);
} // parseInput() method

/**
 * A method to get the amino acid with the index arg.
 * Since no exception checks have been performed we will return the last
 * protein in the chain if given argument is out of bounds.
 * NOTE: This method will create an AminoAcid object if there is no amino acid
 * at given position!! So, double-check whether wanted index is valid!
 *
 * @returns AminoAcid An AminoAcid object
 */
AminoAcid& Protein::get(int arg) {
  if (arg > size()-1) {
    if (size() == 0) {
      AminoAcid tmp;
      _chain.push_back(tmp);
      return _chain.back();
    } else {
      return _chain.back();
    } // else
  } else {
    return _chain[arg];
  }
} // get() method

/**
 * Sets an amino acid at position argIdx. If position is greater than
 * the position of a next element, than this method will insert a new amino
 * acid at next available position, and give that position as return value.
 * NOTE: Check whether wanted index, and returned value are the same.
 */
int Protein::set(AminoAcid& argAcid, int argIdx) {
  if (argIdx < _chain.size()) {
    _chain[argIdx] = argAcid;
    argAcid.id = argIdx;
    return argIdx;
  } else {
    _chain.push_back(argAcid);
    argAcid.id = _chain.size();
    return _chain.size();
  } // else
} // set() method

#ifdef DEBUG
#include <iostream>
void Protein::unittest() {
  Protein o;
  AminoAcid a(o.get(4));
  // Test 1
  // Manual test
  if ((a.x != 0) && (a.y != 0))
    std::cout << "Protein test: ERROR Result>" << a.toString() << std::endl;
  else
    std::cout << "Protein test1: OK Result> " << a.toString() << std::endl;

  // Test 2
  // Using doTest() function
  o.clear();
  o.parseInput("HPH");
  std::string t1 = o.toString();
  doTest("Protein", 2, (t1 == "H,1,1;P,2,1;H,3,1;"), t1.c_str(), t1.c_str());

  std::cout << o.energyString() << std::endl;

  // Let's check the size of the AminoAcid object
  std::cout << "sizeof(AminoAcid) = " << sizeof(AminoAcid) << std::endl;

  // Test if first amino acid is adjacent to the second one
  std::cout << o.adjacentTo(1,1,1) << std::endl; // should print true
  std::cout << o.adjacentTo(1,1,2) << std::endl; // should print false
} // unittest() method
#endif // DEBUG

// $Id: Protein.cpp 42 2009-04-26 04:31:13Z dejan.lekic $
